Add support for composited child windows. (#412882, Ryan Lortie)
authorMatthias Clasen <mclasen@redhat.com>
Fri, 1 Jun 2007 12:16:12 +0000 (12:16 +0000)
committerMatthias Clasen <matthiasc@src.gnome.org>
Fri, 1 Jun 2007 12:16:12 +0000 (12:16 +0000)
2007-06-01  Matthias Clasen  <mclasen@redhat.com>

        Add support for composited child windows.  (#412882, Ryan Lortie)

        * gdk/gdk.symbols:
        * gdk/gdkdisplay.h:
        * gdk/gdkinternals.h:
        * gdk/gdkwindow.[hc]: Add gdk_display_supports_composite() and
        gdk_window_set_composited().

        * gdk/x11/gdkevents-x11.c:
        * gdk/x11/gdkdisplay-x11.[hc]:
        * gdk/x11/gdkwindow-x11.[hc]: X11 implementation.

        * gdk/win32/gdkdisplay-win32.c:
        * gdk/win32/gdkwindow-win32.c: Dummy win32 implementration.

        * gdk/quartz/gdkdisplay-quartz.c:
        * gdk/quartz/gdkwindow-quartz.c: Dummy Quartz implementation.

        * gdk/directfb/gdkdisplay-directfb.c:
        * gdk/directfb/gdkwindow-directfb.c: Dummy DirectFB implementation.

        * tests/testgtk.c: Add a "composited window" test.

svn path=/trunk/; revision=18004

21 files changed:
ChangeLog
docs/reference/ChangeLog
docs/reference/gdk/gdk-sections.txt
docs/reference/gdk/tmpl/windows.sgml
gdk/directfb/gdkdisplay-directfb.c
gdk/directfb/gdkwindow-directfb.c
gdk/gdk.symbols
gdk/gdkdisplay.h
gdk/gdkinternals.h
gdk/gdkwindow.c
gdk/gdkwindow.h
gdk/quartz/gdkdisplay-quartz.c
gdk/quartz/gdkwindow-quartz.c
gdk/win32/gdkdisplay-win32.c
gdk/win32/gdkwindow-win32.c
gdk/x11/gdkdisplay-x11.c
gdk/x11/gdkdisplay-x11.h
gdk/x11/gdkevents-x11.c
gdk/x11/gdkwindow-x11.c
gdk/x11/gdkwindow-x11.h
tests/testgtk.c

index 7e1d588700c943869ea0284e118d9e0d04ea8220..d32636ddad524d333a074be5eedcbb70399ede5c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,28 @@
+2007-06-01  Matthias Clasen  <mclasen@redhat.com>
+
+       Add support for composited child windows.  (#412882, Ryan Lortie)
+
+       * gdk/gdk.symbols:
+       * gdk/gdkdisplay.h:
+       * gdk/gdkinternals.h:
+       * gdk/gdkwindow.[hc]: Add gdk_display_supports_composite() and
+       gdk_window_set_composited().
+
+       * gdk/x11/gdkevents-x11.c:
+       * gdk/x11/gdkdisplay-x11.[hc]:
+       * gdk/x11/gdkwindow-x11.[hc]: X11 implementation.
+
+       * gdk/win32/gdkdisplay-win32.c:
+       * gdk/win32/gdkwindow-win32.c: Dummy win32 implementration.
+
+       * gdk/quartz/gdkdisplay-quartz.c:
+       * gdk/quartz/gdkwindow-quartz.c: Dummy Quartz implementation.
+
+       * gdk/directfb/gdkdisplay-directfb.c:
+       * gdk/directfb/gdkwindow-directfb.c: Dummy DirectFB implementation.
+
+       * tests/testgtk.c: Add a "composited window" test.
+
 2007-06-01  Michael Natterer  <mitch@imendio.com>
 
        * gtk/gtkmenuitem.c (gtk_menu_item_position_menu): don't switch
index f129b25a6f1af3cc103e96e85a0f081e2f868154..282238a233be2c6a5ef449922347154ca5cfdf91 100644 (file)
@@ -1,3 +1,8 @@
+2007-06-01   Matthias Clasen  <mclasen@redhat.com>
+       
+       * gdk/gdk-sections.txt: Add new composited window api
+       * gdk/tmpl/windows.sgml: Add composited window example
+
 2007-05-26   Matthias Clasen  <mclasen@redhat.com>
 
        * gtk/migrating*.sgml: Some cleanups
index 081ce425afea45067ca1d24f7fbf3cf8664111e6..d0828990f5ad747e12c96c1d46aa0740e06ec971 100644 (file)
@@ -155,6 +155,7 @@ gdk_display_supports_clipboard_persistence
 gdk_display_store_clipboard
 gdk_display_supports_shapes
 gdk_display_supports_input_shapes
+gdk_display_supports_composite
 <SUBSECTION Standard>
 GDK_DISPLAY_OBJECT
 GDK_IS_DISPLAY
@@ -651,6 +652,7 @@ gdk_window_unfullscreen
 gdk_window_set_keep_above
 gdk_window_set_keep_below
 gdk_window_set_opacity
+gdk_window_set_composited
 gdk_window_move
 gdk_window_resize
 gdk_window_move_resize
index 25687836f18b7e79921857eb1633f61c47ad2865..6c5d0515bc677f30023aa0d8968c19d940f75429 100644 (file)
@@ -12,6 +12,164 @@ GTK+ level. A #GtkWindow is a toplevel window, the thing a user might think of
 as a "window" with a titlebar and so on; a #GtkWindow may contain many #GdkWindow. 
 For example, each #GtkButton has a #GdkWindow associated with it.
 </para>
+<example id="composited-window-example"><title>Composited windows</title>
+<programlisting><![CDATA[
+#include <gtk/gtk.h>
+
+/* The expose event handler for the event box.
+ *
+ * This function simply draws a transparency onto a widget on the area
+ * for which it receives expose events.  This is intended to give the
+ * event box a "transparent" background.
+ *
+ * In order for this to work properly, the widget must have an RGBA
+ * colourmap.  The widget should also be set as app-paintable since it
+ * doesn't make sense for GTK+ to draw a background if we are drawing it
+ * (and because GTK+ might actually replace our transparency with its
+ * default background colour).
+ */
+static gboolean
+transparent_expose (GtkWidget      *widget,
+                    GdkEventExpose *event)
+{
+  cairo_t *cr;
+
+  cr = gdk_cairo_create (widget->window);
+  cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+  gdk_cairo_region (cr, event->region);
+  cairo_fill (cr);
+  cairo_destroy (cr);
+
+  return FALSE;
+}
+
+/* The expose event handler for the window.
+ *
+ * This function performs the actual compositing of the event box onto
+ * the already-existing background of the window at 50% normal opacity.
+ *
+ * In this case we do not want app-paintable to be set on the widget
+ * since we want it to draw its own (red) background. Because of this,
+ * however, we must ensure that we use g_signal_register_after so that
+ * this handler is called after the red has been drawn. If it was
+ * called before then GTK would just blindly paint over our work.
+ *
+ * Note: if the child window has children, then you need a cairo 1.16
+ * feature to make this work correctly.
+ */
+static gboolean
+window_expose_event (GtkWidget      *widget,
+                     GdkEventExpose *event)
+{
+  GdkRegion *region;
+  GtkWidget *child;
+  cairo_t *cr;
+
+  /* get our child (in this case, the event box) */ 
+  child = gtk_bin_get_child (GTK_BIN (widget));
+
+  /* create a cairo context to draw to the window */
+  cr = gdk_cairo_create (widget->window);
+
+  /* the source data is the (composited) event box */
+  gdk_cairo_set_source_pixmap (cr, child->window,
+                               child->allocation.x,
+                               child->allocation.y);
+
+  /* draw no more than our expose event intersects our child */
+  region = gdk_region_rectangle (&child->allocation);
+  gdk_region_intersect (region, event->region);
+  gdk_cairo_region (cr, region);
+  cairo_clip (cr);
+
+  /* composite, with a 50% opacity */
+  cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+  cairo_paint_with_alpha (cr, 0.5);
+
+  /* we're done */
+  cairo_destroy (cr);
+
+  return FALSE;
+}
+
+int
+main (int argc, char **argv)
+{
+  GtkWidget *window, *event, *button;
+  GdkScreen *screen;
+  GdkColormap *rgba;
+  GdkColor red;
+
+  gtk_init (&argc, &argv);
+
+  /* Make the widgets */
+  button = gtk_button_new_with_label ("A Button");
+  event = gtk_event_box_new ();
+  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+  /* Put a red background on the window */
+  gdk_color_parse ("red", &red);
+  gtk_widget_modify_bg (window, GTK_STATE_NORMAL, &red);
+
+  /* Set the colourmap for the event box.
+   * Must be done before the event box is realised.
+   */
+  screen = gtk_widget_get_screen (event);
+  rgba = gdk_screen_get_rgba_colormap (screen);
+  gtk_widget_set_colormap (event, rgba);
+
+  /* Set our event box to have a fully-transparent background
+   * drawn on it. Currently there is no way to simply tell GTK+
+   * that "transparency" is the background colour for a widget.
+   */
+  gtk_widget_set_app_paintable (GTK_WIDGET (event), TRUE);
+  g_signal_connect (event, "expose-event",
+                    G_CALLBACK (transparent_expose), NULL);
+
+  /* Put them inside one another */
+  gtk_container_set_border_width (GTK_CONTAINER (window), 10);
+  gtk_container_add (GTK_CONTAINER (window), event);
+  gtk_container_add (GTK_CONTAINER (event), button);
+
+  /* Realise and show everything */
+  gtk_widget_show_all (window);
+
+  /* Set the event box GdkWindow to be composited.
+   * Obviously must be performed after event box is realised.
+   */
+  gdk_window_set_composited (event->window, TRUE);
+
+  /* Set up the compositing handler.
+   * Note that we do _after_ so that the normal (red) background is drawn
+   * by gtk before our compositing occurs.
+   */
+  g_signal_connect_after (window, "expose-event",
+                          G_CALLBACK (window_expose_event), NULL);
+
+  gtk_main (<!-- -->);
+
+  return 0;
+}
+]]>
+</programlisting></example>
+<para>
+In the example <xref linkend="composited-window-example"/>, a button is 
+placed inside of an event box inside of a window. The event box is 
+set as composited and therefore is no longer automatically drawn to 
+the screen. 
+</para>
+<para>
+When the contents of the event box change, an expose event is
+generated on its parent window (which, in this case, belongs to
+the toplevel #GtkWindow). The expose handler for this widget is
+responsible for merging the changes back on the screen in the way
+that it wishes.
+</para>
+<para>
+In our case, we merge the contents with a 50% transparency. We
+also set the background colour of the window to red. The effect is
+that the background shows through the button. 
+</para>
 
 <!-- ##### SECTION See_Also ##### -->
 <para>
@@ -465,6 +623,15 @@ Deprecated equivalent of g_object_unref()
 @opacity: 
 
 
+<!-- ##### FUNCTION gdk_window_set_composited ##### -->
+<para>
+
+</para>
+
+@window: 
+@composited: 
+
+
 <!-- ##### FUNCTION gdk_window_move ##### -->
 <para>
 
index 9cf7a2fdb1723361d0566678e2d36f3bfb449192..04275fee27a1101a7dbf0f296812baeb9d0b54e6 100644 (file)
@@ -511,6 +511,13 @@ gdk_notify_startup_complete_with_id (const gchar* startup_id)
 {
 }
 
+
+gboolean
+gdk_display_supports_composite (GdkDisplay *display)
+{
+    return FALSE;
+}
+
 #define __GDK_DISPLAY_X11_C__
 #include "gdkaliasdef.c"
 
index 59e28b7393fc36d6630cdbcb6495b36396bcefa5..7fe39118529afd50c9e473c28c8ee7c151e1b80c 100644 (file)
@@ -3038,6 +3038,14 @@ gdk_window_set_opacity (GdkWindow *window,
   cardinal = opacity * 0xff;
   gdk_directfb_window_set_opacity(window,cardinal);
 }
+
+void
+_gdk_windowing_window_set_composited (GdkWindow *window,
+                                      gboolean   composited)
+{
+}
+
+
 #define __GDK_WINDOW_X11_C__
 #include "gdkaliasdef.c"
 
index 56722e7cb684358b9318f830e5467a6a524cd6fe..8757324fcc7e6c53a62499c3461acc2751e84940 100644 (file)
@@ -466,6 +466,7 @@ gdk_display_supports_clipboard_persistence
 gdk_display_supports_selection_notification
 gdk_display_supports_shapes
 gdk_display_supports_input_shapes
+gdk_display_supports_composite
 #endif
 #endif
 
@@ -673,6 +674,7 @@ gdk_window_remove_filter
 gdk_window_set_debug_updates
 gdk_window_set_user_data
 gdk_window_thaw_updates
+gdk_window_set_composited
 #endif
 #endif
 
index c627a085810e8be12f8636bd8549b845ae3a0844..b55b312257ee8c2364f14bc8d37a484882659ff3 100644 (file)
@@ -181,6 +181,7 @@ void     gdk_display_store_clipboard                (GdkDisplay *display,
 
 gboolean gdk_display_supports_shapes           (GdkDisplay    *display);
 gboolean gdk_display_supports_input_shapes     (GdkDisplay    *display);
+gboolean gdk_display_supports_composite        (GdkDisplay    *display);
 
 G_END_DECLS
 
index 1c279f4ec3606c77122dac3be8dc654e7b2c1806..f97b97be043b401df5ca1579b6be083a0e5abdd4 100644 (file)
@@ -337,6 +337,9 @@ void _gdk_windowing_window_destroy_foreign (GdkWindow *window);
 void _gdk_windowing_display_set_sm_client_id (GdkDisplay  *display,
                                              const gchar *sm_client_id);
 
+void _gdk_windowing_window_set_composited (GdkWindow *window,
+                                          gboolean composited);
+
 #define GDK_TYPE_PAINTABLE            (_gdk_paintable_get_type ())
 #define GDK_PAINTABLE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_PAINTABLE, GdkPaintable))
 #define GDK_IS_PAINTABLE(obj)        (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_PAINTABLE))
index d0d1573a6efbb0f9b927693f9b30898bf933601a..5e1d8306988c8346be67a8190f2c95b9c2db6cbc 100644 (file)
@@ -1042,6 +1042,7 @@ gdk_window_end_paint (GdkWindow *window)
 {
 #ifdef USE_BACKING_STORE
   GdkWindowObject *private = (GdkWindowObject *)window;
+  GdkWindowObject *composited;
   GdkWindowPaint *paint;
   GdkGC *tmp_gc;
   GdkRectangle clip_box;
@@ -1094,6 +1095,34 @@ gdk_window_end_paint (GdkWindow *window)
   g_object_unref (paint->pixmap);
   gdk_region_destroy (paint->region);
   g_free (paint);
+
+  /* find a composited window in our hierarchy to signal its
+   * parent to redraw, calculating the clip box as we go...
+   *
+   * stop if parent becomes NULL since then we'd have nowhere
+   * to draw (ie: 'composited' will always be non-NULL here).
+   */
+  for (composited = private;
+       composited->parent;
+       composited = composited->parent)
+    {
+      int width, height;
+
+      gdk_drawable_get_size (GDK_DRAWABLE (composited->parent),
+                            &width, &height);
+
+      clip_box.x += composited->x;
+      clip_box.y += composited->y;
+      clip_box.width = MIN (clip_box.width, width - clip_box.x);
+      clip_box.height = MIN (clip_box.height, height - clip_box.y);
+
+      if (composited->composited)
+       {
+         gdk_window_invalidate_rect (GDK_WINDOW (composited->parent),
+                                     &clip_box, FALSE);
+         break;
+       }
+    }
 #endif /* USE_BACKING_STORE */
 }
 
@@ -2601,7 +2630,8 @@ gdk_window_invalidate_maybe_recurse (GdkWindow *window,
          child_region = gdk_region_rectangle (&child_rect);
          
          /* remove child area from the invalid area of the parent */
-         if (GDK_WINDOW_IS_MAPPED (child) && !child->shaped)
+         if (GDK_WINDOW_IS_MAPPED (child) && !child->shaped &&
+             !child->composited)
            gdk_region_subtract (visible_region, child_region);
          
          if (child_func && (*child_func) ((GdkWindow *)child, user_data))
@@ -3056,5 +3086,64 @@ gdk_window_foreign_new (GdkNativeWindow anid)
   return gdk_window_foreign_new_for_display (gdk_display_get_default (), anid);
 }
 
+/**
+ * gdk_window_set_composited:
+ * @window: a #GdkWindow
+ *
+ * Sets a #GdkWindow as composited. Composited windows do
+ * not automatically have their contents drawn to the screen.
+ * Drawing is redirected to an offscreen buffer and an expose
+ * event is emitted on the parent of the composited window.
+ * It is the responsibility of the parent's expose handler to
+ * manually merge the off-screen content onto the screen in
+ * whatever way it sees fit. See <xref linkend="composited-window-example"/>
+ * for an example.
+ *
+ * It only makes sense for child windows to be composited; see
+ * gdk_window_set_opacity() if you need translucent toplevel
+ * windows.
+ *
+ * An additional effect of this call is that the area of this
+ * window is no longer clipped from regions marked for
+ * invalidation on its parent. Draws done on the parent
+ * window are also no longer clipped by the child.
+ *
+ * This call is only supported on some systems (currently,
+ * only X11 with new enough Xcomposite and Xdamage extensions). 
+ * You must call gdk_display_supports_composite() to check if
+ * setting a window as composited is supported before
+ * attempting to do so.
+ *
+ * Since: 2.12
+ */
+void
+gdk_window_set_composited (GdkWindow *window,
+                           gboolean   composited)
+{
+  GdkWindowObject *private = (GdkWindowObject *)window;
+  GdkDisplay *display;
+
+  g_return_if_fail (window != NULL);
+  g_return_if_fail (GDK_IS_WINDOW (window));
+
+  composited = composited != FALSE;
+
+  if (private->composited == composited)
+    return;
+
+  display = gdk_drawable_get_display (GDK_DRAWABLE (window));
+
+  if (!gdk_display_supports_composite (display) && composited)
+    {
+      g_warning ("gdk_window_set_composited called but "
+                "compositing is not supported");
+      return;
+    }
+
+  _gdk_windowing_window_set_composited (window, composited);
+
+  private->composited = composited;
+}
+
 #define __GDK_WINDOW_C__
 #include "gdkaliasdef.c"
index 9ff3a09fca0622c06fef28336d128f1ccf29a496..d50d3e6c9a2c126e687b5adc03543dd26d6fd9e2 100644 (file)
@@ -289,6 +289,7 @@ struct _GdkWindowObject
   guint guffaw_gravity : 1;
   guint input_only : 1;
   guint modal_hint : 1;
+  guint composited : 1;
   
   guint destroyed : 2;
 
@@ -394,6 +395,9 @@ void gdk_window_shape_combine_region (GdkWindow         *window,
  */
 void gdk_window_set_child_shapes (GdkWindow *window);
 
+void gdk_window_set_composited   (GdkWindow *window,
+                                  gboolean composited);
+
 /*
  * This routine allows you to merge (ie ADD) child shapes to your
  * own window's shape keeping its current shape and ADDING the child
index b34567eaf77902c40b1f5c9dacb2841095aed2e3..3f9a01f254fbac106f48e77ec7af06d52835bfcf 100644 (file)
@@ -169,3 +169,11 @@ gdk_display_store_clipboard (GdkDisplay *display,
 {
   /* FIXME: Implement */
 }
+
+
+gboolean
+gdk_display_supports_composite (GdkDisplay *display)
+{
+  /* FIXME: Implement */
+  return FALSE;
+}
index 31f2b01a39edfa128c084bbc590392ed63870d47..51f253ecf231a36b12d6a050102185734a571573 100644 (file)
@@ -2155,3 +2155,8 @@ gdk_window_set_opacity (GdkWindow *window,
 
   [impl->toplevel setAlphaValue: opacity];
 }
+
+void
+_gdk_windowing_window_set_composited (GdkWindow *window, gboolean composited)
+{
+}
index 5b77fb11bba4ebff49b30b0bb8137a22e0f17670..78c26bf779472d0722988c5742c7512c6c5b22ad 100644 (file)
@@ -389,3 +389,9 @@ gdk_display_supports_input_shapes (GdkDisplay *display)
 
   return FALSE;
 }
+
+gboolean
+gdk_display_supports_composite (GdkDisplay *display)
+{
+  return FALSE;
+}
index 631d4200d1bc3e043f7d226d4ddae7471e96706f..fad5644a3b6d2dc7ff01cf057959f0e589d52198 100644 (file)
@@ -3571,3 +3571,8 @@ gdk_window_set_opacity (GdkWindow *window,
                                         opacity * 0xff,
                                         LWA_ALPHA));
 }
+
+void
+_gdk_windowing_window_set_composited (GdkWindow *window, gboolean composited)
+{
+}
index c57ef2e833feb2aa6fa994806f4207ec4c8da00a..de7c1f2f8336f14af6f7f4528f2b24a19167fae4 100644 (file)
 #include <X11/extensions/shape.h>
 #endif
 
+#ifdef HAVE_XCOMPOSITE
+#include <X11/extensions/Xcomposite.h>
+#endif
+
+#ifdef HAVE_XDAMAGE
+#include <X11/extensions/Xdamage.h>
+#endif
+
 
 static void   gdk_display_x11_dispose            (GObject            *object);
 static void   gdk_display_x11_finalize           (GObject            *object);
@@ -206,6 +214,29 @@ gdk_display_open (const gchar *display_name)
 #endif
     display_x11->have_xfixes = FALSE;
 
+#ifdef HAVE_XCOMPOSITE
+  if (XCompositeQueryExtension (display_x11->xdisplay,
+                               &ignore, &ignore))
+      display_x11->have_xcomposite = TRUE;
+  else
+#endif
+    display_x11->have_xcomposite = FALSE;
+
+#ifdef HAVE_XDAMAGE
+  if (XDamageQueryExtension (display_x11->xdisplay,
+                            &display_x11->xdamage_event_base,
+                            &ignore))
+    {
+      display_x11->have_xdamage = TRUE;
+
+      gdk_x11_register_standard_event_type (display,
+                                           display_x11->xdamage_event_base,
+                                           XDamageNumberEvents);
+    }
+  else
+#endif
+    display_x11->have_xdamage = FALSE;
+
   display_x11->have_shapes = FALSE;
   display_x11->have_input_shapes = FALSE;
 #ifdef HAVE_SHAPE_EXT
@@ -1363,5 +1394,30 @@ gdk_x11_display_get_startup_notification_id (GdkDisplay *display)
   return GDK_DISPLAY_X11 (display)->startup_notification_id;
 }
 
+/**
+ * gdk_display_supports_composite:
+ * @display: a #GdkDisplay
+ *
+ * Returns %TRUE if gdk_window_set_composited() can be used
+ * to redirect drawing on the window using compositing.
+ *
+ * Currently this only works on X11 with XComposite and
+ * XDamage extensions available.
+ *
+ * Returns: %TRUE if windows may be composited.
+ *
+ * Since: 2.12
+ */
+gboolean
+gdk_display_supports_composite (GdkDisplay *display)
+{
+  GdkDisplayX11 *x11_display = GDK_DISPLAY_X11 (display);
+
+  return x11_display->have_xcomposite &&
+        x11_display->have_xdamage &&
+        x11_display->have_xfixes;
+}
+
+
 #define __GDK_DISPLAY_X11_C__
 #include "gdkaliasdef.c"
index 14b8fd5b293262593b09137cd5f55fee3800ff9d..7b2654cd7c9901576daadbcd4d5e43ddecc080a8 100644 (file)
@@ -81,6 +81,10 @@ struct _GdkDisplayX11
   gboolean have_xfixes;
   gint xfixes_event_base;
 
+  gboolean have_xcomposite;
+  gboolean have_xdamage;
+  gint xdamage_event_base;
+
   /* If the SECURITY extension is in place, whether this client holds 
    * a trusted authorization and so is allowed to make various requests 
    * (grabs, properties etc.) Otherwise always TRUE. */
index 6df08bb3df4390f4259e1d4ea76a7f27fb203e92..b9560d46ed0053b95195519cdbb9a3e8ef944d23 100644 (file)
@@ -2101,6 +2101,34 @@ gdk_event_translate (GdkDisplay *display,
        }
       else
 #endif
+#if defined(HAVE_XCOMPOSITE) && defined (HAVE_XDAMAGE) && defined (HAVE_XFIXES)
+      if (display_x11->have_xdamage && window_private->composited &&
+         xevent->type == display_x11->xdamage_event_base + XDamageNotify)
+       {
+         XDamageNotifyEvent *damage_event = (XDamageNotifyEvent *) xevent;
+         XserverRegion repair;
+         GdkRectangle rect;
+
+         rect.x = window_private->x + damage_event->area.x;
+         rect.y = window_private->y + damage_event->area.y;
+         rect.width = damage_event->area.width;
+         rect.height = damage_event->area.height;
+
+         repair = XFixesCreateRegion (display_x11->xdisplay,
+                                     &damage_event->area, 1);
+         XDamageSubtract (display_x11->xdisplay,
+                          window_impl->damage,
+                          repair, None);
+         XFixesDestroyRegion (display_x11->xdisplay, repair);
+
+         if (window_private->parent != NULL)
+           _gdk_window_process_expose (GDK_WINDOW (window_private->parent),
+                                       damage_event->serial, &rect);
+
+         return_val = TRUE;
+       }
+      else
+#endif
        {
          /* something else - (e.g., a Xinput event) */
          
index e3e84270d6c1b7022544598f1f1cd2d6121257e8..a58de4526d315b2226af5795c6b9db335b4d200f 100644 (file)
 #include <X11/extensions/shape.h>
 #endif
 
+#ifdef HAVE_XCOMPOSITE
+#include <X11/extensions/Xcomposite.h>
+#endif
+
+#ifdef HAVE_XFIXES
+#include <X11/extensions/Xfixes.h>
+#endif
+
+#ifdef HAVE_XDAMAGE
+#include <X11/extensions/Xdamage.h>
+#endif
+
 const int _gdk_event_mask_table[21] =
 {
   ExposureMask,
@@ -185,6 +197,14 @@ gdk_window_impl_x11_finalize (GObject *object)
 
   _gdk_xgrab_check_destroy (GDK_WINDOW (wrapper));
 
+#if defined(HAVE_XCOMPOSITE) && defined(HAVE_XDAMAGE) && defined (HAVE_XFIXES)
+  if (window_impl->damage != None)
+  {
+    XDamageDestroy (GDK_WINDOW_XDISPLAY (object), window_impl->damage);
+    window_impl->damage = None;
+  }
+#endif
+
   if (!GDK_WINDOW_DESTROYED (wrapper))
     {
       GdkDisplay *display = GDK_WINDOW_DISPLAY (wrapper);
@@ -6413,10 +6433,14 @@ gdk_window_beep (GdkWindow *window)
  *
  * Request the windowing system to make @window partially transparent,
  * with opacity 0 being fully transparent and 1 fully opaque. (Values
- * of the opacity parameter are clamped to the [0,1] range.) On X11
- * this works only on X screens with a compositing manager running.
+ * of the opacity parameter are clamped to the [0,1] range.) 
+ *
+ * On X11, this works only on X screens with a compositing manager 
+ * running.
  *
  * For setting up per-pixel alpha, see gdk_screen_get_rgba_colormap().
+ * For making non-toplevel windows translucent, see 
+ * gdk_window_set_composited().
  *
  * Since: 2.12
  */
@@ -6455,5 +6479,39 @@ gdk_window_set_opacity (GdkWindow *window,
                     (guchar *) cardinal, 1);
 }
 
+void
+_gdk_windowing_window_set_composited (GdkWindow *window,
+                                      gboolean   composited)
+{
+#if defined(HAVE_XCOMPOSITE) && defined(HAVE_XDAMAGE) && defined (HAVE_XFIXES)
+  GdkWindowObject *private = (GdkWindowObject *) window;
+  GdkDisplayX11 *x11_display;
+  GdkWindowImplX11 *impl;
+  GdkDisplay *display;
+  Display *dpy;
+  Window xid;
+
+  impl = GDK_WINDOW_IMPL_X11 (private->impl);
+
+  display = gdk_screen_get_display (GDK_DRAWABLE_IMPL_X11 (impl)->screen);
+  x11_display = GDK_DISPLAY_X11 (display);
+  dpy = GDK_DISPLAY_XDISPLAY (display);
+  xid = GDK_WINDOW_XWINDOW (private);
+
+  if (composited)
+    {
+      XCompositeRedirectWindow (dpy, xid, CompositeRedirectManual);
+      impl->damage = XDamageCreate (dpy, xid, XDamageReportBoundingBox);
+    }
+  else
+    {
+      XCompositeUnredirectWindow (dpy, xid, CompositeRedirectManual);
+      XDamageDestroy (dpy, impl->damage);
+      impl->damage = None;
+    }
+#endif
+}
+
+
 #define __GDK_WINDOW_X11_C__
 #include "gdkaliasdef.c"
index 18fa23a0575340a6917ce56a6f7ae502b5ad6637..3ab23d60031a4aa9ea5cc32ef0b8a31c30620950 100644 (file)
 
 #include <gdk/x11/gdkdrawable-x11.h>
 
+#ifdef HAVE_XDAMAGE
+#include <X11/extensions/Xdamage.h>
+#endif
+
 #ifdef HAVE_XSYNC
 #include <X11/extensions/sync.h>
 #endif
@@ -79,6 +83,10 @@ struct _GdkWindowImplX11
   gint8 toplevel_window_type;
   guint override_redirect : 1;
   guint use_synchronized_configure : 1;
+
+#if defined (HAVE_XCOMPOSITE) && defined(HAVE_XDAMAGE) && defined (HAVE_XFIXES)
+  Damage damage;
+#endif
 };
  
 struct _GdkWindowImplX11Class 
index 82894e970613e66ec771f5639282fea25e8ec2dd..44eb4fcc50ee0529460a865feeb11838a8c9ad68 100644 (file)
@@ -420,6 +420,146 @@ create_alpha_window (GtkWidget *widget)
     gtk_widget_destroy (window);
 }
 
+/*
+ * Composited non-toplevel window
+ */
+
+/* The expose event handler for the event box.
+ *
+ * This function simply draws a transparency onto a widget on the area
+ * for which it receives expose events.  This is intended to give the
+ * event box a "transparent" background.
+ *
+ * In order for this to work properly, the widget must have an RGBA
+ * colourmap.  The widget should also be set as app-paintable since it
+ * doesn't make sense for GTK to draw a background if we are drawing it
+ * (and because GTK might actually replace our transparency with its
+ * default background colour).
+ */
+static gboolean
+transparent_expose (GtkWidget *widget,
+                    GdkEventExpose *event)
+{
+  cairo_t *cr;
+
+  cr = gdk_cairo_create (widget->window);
+  cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+  gdk_cairo_region (cr, event->region);
+  cairo_fill (cr);
+  cairo_destroy (cr);
+
+  return FALSE;
+}
+
+/* The expose event handler for the window.
+ *
+ * This function performs the actual compositing of the event box onto
+ * the already-existing background of the window at 50% normal opacity.
+ *
+ * In this case we do not want app-paintable to be set on the widget
+ * since we want it to draw its own (red) background.  Because of this,
+ * however, we must ensure that we use g_signal_register_after so that
+ * this handler is called after the red has been drawn.  If it was
+ * called before then GTK would just blindly paint over our work.
+ */
+static gboolean
+window_expose_event (GtkWidget *widget,
+                     GdkEventExpose *event)
+{
+  GdkRegion *region;
+  GtkWidget *child;
+  cairo_t *cr;
+
+  /* get our child (in this case, the event box) */ 
+  child = gtk_bin_get_child (GTK_BIN (widget));
+
+  /* create a cairo context to draw to the window */
+  cr = gdk_cairo_create (widget->window);
+
+  /* the source data is the (composited) event box */
+  gdk_cairo_set_source_pixmap (cr, child->window,
+                               child->allocation.x,
+                               child->allocation.y);
+
+  /* draw no more than our expose event intersects our child */
+  region = gdk_region_rectangle (&child->allocation);
+  gdk_region_intersect (region, event->region);
+  gdk_cairo_region (cr, region);
+  cairo_clip (cr);
+
+  /* composite, with a 50% opacity */
+  cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+  cairo_paint_with_alpha (cr, 0.5);
+
+  /* we're done */
+  cairo_destroy (cr);
+
+  return FALSE;
+}
+
+void
+create_composited_window (GtkWidget *widget)
+{
+  static GtkWidget *window;
+
+  if (!window)
+    {
+      GtkWidget *event, *button;
+      GdkScreen *screen;
+      GdkColormap *rgba;
+      GdkColor red;
+
+      /* make the widgets */
+      button = gtk_button_new_with_label ("A Button");
+      event = gtk_event_box_new ();
+      window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+      /* put a red background on the window */
+      gdk_color_parse ("red", &red);
+      gtk_widget_modify_bg (window, GTK_STATE_NORMAL, &red);
+
+      /* set the colourmap for the event box.
+       * must be done before the event box is realised.
+       */
+      screen = gtk_widget_get_screen (event);
+      rgba = gdk_screen_get_rgba_colormap (screen);
+      gtk_widget_set_colormap (event, rgba);
+
+      /* set our event box to have a fully-transparent background
+       * drawn on it.  currently there is no way to simply tell gtk
+       * that "transparency" is the background colour for a widget.
+       */
+      gtk_widget_set_app_paintable (GTK_WIDGET (event), TRUE);
+      g_signal_connect (event, "expose-event",
+                        G_CALLBACK (transparent_expose), NULL);
+
+      /* put them inside one another */
+      gtk_container_set_border_width (GTK_CONTAINER (window), 10);
+      gtk_container_add (GTK_CONTAINER (window), event);
+      gtk_container_add (GTK_CONTAINER (event), button);
+
+      /* realise and show everything */
+      gtk_widget_realize (button);
+
+      /* set the event box GdkWindow to be composited.
+       * obviously must be performed after event box is realised.
+       */
+      gdk_window_set_composited (event->window, TRUE);
+
+      /* set up the compositing handler.
+       * note that we do _after so that the normal (red) background is drawn
+       * by gtk before our compositing occurs.
+       */
+      g_signal_connect_after (window, "expose-event",
+                              G_CALLBACK (window_expose_event), NULL);
+    }
+
+  if (!GTK_WIDGET_VISIBLE (window))
+    gtk_widget_show_all (window);
+  else
+    gtk_widget_destroy (window);
+}
+
 /*
  * Big windows and guffaw scrolling
  */
@@ -13290,6 +13430,7 @@ struct {
   { "check buttons", create_check_buttons },
   { "clist", create_clist},
   { "color selection", create_color_selection },
+  { "composited window", create_composited_window },
   { "ctree", create_ctree },
   { "cursors", create_cursors },
   { "dialog", create_dialog, TRUE },